####################################################-*- coding: utf-8 -*-#####
#
#
# Basic database operation and maintaining, all data parser should call the
# methods provided by this module to interact with DB.
# This class is a singleton
# 
#
##############################################################################
import time
import mysql
from threading import Thread, Lock, Event
import utils


class Database(Thread, BaseException):
    __single = None
    """All the enqueue* function are used to enqueue data to specifical
       tables, and their naming rule is enqueue + table name.
    """
    def __init__(self):
        if Database.__single:
            raise Database.__single
        
        Thread.__init__(self)
        Database.__single = self
        self.lock  = Lock()
        self.signal = Event()
        self.queue = []         # [] is list, {} is dict
        self.cursor = None

    def __del__(self):
        if self.cursor:
            self.cursor.close()

    @classmethod
    def getInstance(cls):
        try:
            single = cls()
        except cls as s:
            single = s
        return single

    def exit(self):
        # FIXME: busy wait, sleep 1s
        while len(self.queue):
            time.sleep(1)
            
        self.signal.set()       # signal the empty queue wait if it's there
        self.running = False
        
    def run(self):
        """ Query sql statement queue and execute sql statement in the queue.
            If queue is empty, the wait an signal(from parser thread) and then
            to wake up.
        """
        empty = False
        while True:
            self.lock.acquire() # we need visit the queue exclusively
            if len(self.queue) == 0:
                empty = True
            else:
                empty = False
                sql = self.queue.pop()
            self.lock.release()

            if empty:
                self.signal.wait()
                self.signal.clear()
            else:
                try:
                    self.cursor.execute(sql)
                except Exception as msg:
                    utils.error('%s'%msg)

            
    ##########################################################################
    # public enqueue function, invoke corrsponding internal enqueue functions
    # accroding to command type
    ##########################################################################
    def enqueueData(self, cmd_type, format, data, code=None):
        """
            'cmd_type' tells us which tables should be operated.
            'format' is the column for each data_source.
            'data' is a list of multi rows data form in data_format.
            if 'code' is given, and 'cmd_type' is 'daily_trading'
        """
        try:                    # a lambda implemented switch...case
            {
                'basic_info': lambda: enqueueBasicInfo(format, data),
                'ext_info'  : lambda: enqueueExtInfo(format, data),
                'candle'    : lambda: enqueueCandle(format, data),
                'industry'  : lambda: enqueueIndustry(format, data),
                'market'    : lambda: enqueueMarket(format, data),
                'bulletin'  : lambda: enqueueBulletin(format, data),
                'cash_bonus' : lambda: enqueueCashBonus(format, data),
                'share_bonus' : lambda: enqueueShareBonus(format, data),
                'industry_pe' : lambda: enqueueIndustryPE(format, data),
                'share_volume' : lambda: enqueueShareVolume(format, data),
                'daily_trading' : lambda: enqueueDailyTrading(code, format, data)
            }[cmd_type]()
        except KeyError:
            # default action
            utils.error('Unknown command type "%s"'%cmd_type)
    
    ##########################################################################
    # internal enqueue function
    ##########################################################################
            
    def enqueueBasicInfo(self, format, source):
        """ concate sql statement based format and source. """
        sql = {}
        self.lock.acquire()
        self.queue.append(sql)
        self.signal.set()        
        self.lock.release()

    def enqueueExtInfo(self, format, source):
        """ concate sql statement based format and source. """ 
        sql = {}
        self.lock.acquire()
        self.queue.append(sql)
        self.signal.set()        
        self.lock.release()        

    def enqueueCandle(self, format, source):
        """ concate sql statement based format and source. """ 
        sql = {}
        self.lock.acquire()
        self.queue.append(sql)
        self.signal.set()        
        self.lock.release()

    def enqueueIndustry(self, format, source):
        """ concate sql statement based format and source. """ 
        sql = {}
        self.lock.acquire()
        self.queue.append(sql)
        self.signal.set()        
        self.lock.release()

    def enqueueMarket(self, format, source):
        """ concate sql statement based format and source. """
        sql = {}
        self.lock.acquire()
        self.queue.append(sql)
        self.signal.set()        
        self.lock.release()

    def enqueueBulletin(self, format, source):
        """ concate sql statement based format and source. """        
        sql = {}
        self.lock.acquire()
        self.queue.append(sql)
        self.signal.set()        
        self.lock.release()

    def enqueueCashBonus(self, format, source):
        """ concate sql statement based format and source. """        
        sql = {}
        self.lock.acquire()
        self.queue.append(sql)
        self.signal.set()
        self.lock.release()

    def enqueueShareBonus(self, format, source):
        """ concate sql statement based format and source. """        
        sql = {}
        self.lock.acquire()
        self.queue.append(sql)
        self.signal.set()        
        self.lock.release()

    def enqueueIndustryPE(self, format, source):
        """ concate sql statement based format and source. """        
        sql = {}
        self.lock.acquire()
        self.queue.append(sql)
        self.signal.set()        
        self.lock.release()

    def enqueueShareVolume(self, format, source):
        """ concate sql statement based format and source. """
        sql = {}
        self.lock.acquire()
        self.queue.append(sql)
        self.signal.set()        
        self.lock.release()

    
    def enqueueDailyTrading(self, code, format, source):
        """ concate sql statement based format and source. """        
        sql = {}
        self.lock.acquire()
        self.queue.append(sql)
        self.signal.set()
        self.lock.release()
        

    
